﻿#include "AMeshVS_DataSource.h"

IMPLEMENT_STANDARD_RTTIEXT(AMeshVS_DataSource, MeshVS_DataSource)

AMeshVS_DataSource::AMeshVS_DataSource(const Handle(Poly_Triangulation) & plyTri) {
    // 将三角化结果转换为网格数据
    if(!plyTri.IsNull()) {
        const Standard_Integer aNbNodes = plyTri->NbNodes();

        // 使用 2 维数组 m_nodeCoords 保存节点坐标，m_nodes 保存节点索引
        m_nodeCoords = new TColStd_HArray2OfReal(1, aNbNodes, 1, 3);
        for(Standard_Integer i = 1; i <= aNbNodes; i++) {
            m_nodes.Add(i);
            gp_Pnt xyz = plyTri->Node(i);

            m_nodeCoords->SetValue(i, 1, xyz.X());
            m_nodeCoords->SetValue(i, 2, xyz.Y());
            m_nodeCoords->SetValue(i, 3, xyz.Z());
        }

        const Standard_Integer aNbTris = plyTri->NbTriangles();

        // 保存三角面法向坐标和顶点索引
        m_elemNormals = new TColStd_HArray2OfReal(1, aNbTris, 1, 3);
        m_elemNodes = new TColStd_HArray2OfInteger(1, aNbTris, 1, 3);
        for(Standard_Integer i = 1; i <= aNbTris; i++) {
            // 保存三角面索引
            m_elements.Add(i);

            const Poly_Triangle aTri = plyTri->Triangle(i);

            // 获得三角面顶点索引
            Standard_Integer V[3];
            aTri.Get(V[0], V[1], V[2]);

            const gp_Pnt aP1 = plyTri->Node(V[0]);
            const gp_Pnt aP2 = plyTri->Node(V[1]);
            const gp_Pnt aP3 = plyTri->Node(V[2]);

            gp_Vec aV1(aP1, aP2);
            gp_Vec aV2(aP2, aP3);

            // 计算法向
            gp_Vec aN = aV1.Crossed(aV2);
            if(aN.SquareMagnitude() > Precision::SquareConfusion())
                aN.Normalize();
            else
                aN.SetCoord(0.0, 0.0, 0.0);

            for(Standard_Integer j = 0; j < 3; j++) m_elemNodes->SetValue(i, j + 1, V[j]);

            m_elemNormals->SetValue(i, 1, aN.X());
            m_elemNormals->SetValue(i, 2, aN.Y());
            m_elemNormals->SetValue(i, 3, aN.Z());
        }
    }
}

Standard_Boolean AMeshVS_DataSource::GetGeom(const Standard_Integer ID, const Standard_Boolean IsElement, TColStd_Array1OfReal& Coords, Standard_Integer& NbNodes, MeshVS_EntityType& Type) const {
    if(m_nodes.Extent() == 0) return Standard_False;

    if(IsElement) {
        // 根据 ID 获得三角面
        if(ID >= 1 && ID <= m_elements.Extent()) {
            Type = MeshVS_ET_Face;
            NbNodes = 3;

            for(Standard_Integer i = 1, k = 1; i <= 3; i++) {
                Standard_Integer IdxNode = m_elemNodes->Value(ID, i);
                for(Standard_Integer j = 1; j <= 3; j++, k++) Coords(k) = m_nodeCoords->Value(IdxNode, j);
            }

            return Standard_True;
        } else
            return Standard_False;
    } else if(ID >= 1 && ID <= m_nodes.Extent()) {
        // 根据 ID 获得顶点
        Type = MeshVS_ET_Node;
        NbNodes = 1;

        Coords(1) = m_nodeCoords->Value(ID, 1);
        Coords(2) = m_nodeCoords->Value(ID, 2);
        Coords(3) = m_nodeCoords->Value(ID, 3);
        return Standard_True;
    } else
        return Standard_False;
}

Standard_Boolean AMeshVS_DataSource::GetGeomType(const Standard_Integer theID, const Standard_Boolean IsElement, MeshVS_EntityType& theType) const {
    if(m_nodes.Extent() == 0) return Standard_False;

    if(IsElement) {
        if(theID >= 1 && theID <= m_elements.Extent()) {
            theType = MeshVS_ET_Face;
            return Standard_True;
        }
    } else if(theID >= 1 && theID <= m_nodes.Extent()) {
        theType = MeshVS_ET_Node;
        return Standard_True;
    }
    return Standard_False;
}

Standard_Address AMeshVS_DataSource::GetAddr(const Standard_Integer /*ID*/, const Standard_Boolean /*IsElement*/) const {
    return NULL;
}

Standard_Boolean AMeshVS_DataSource::GetNodesByElement(const Standard_Integer ID, TColStd_Array1OfInteger& theNodeIDs, Standard_Integer& /*theNbNodes*/) const {
    if(m_nodes.Extent() == 0) return Standard_False;

    // 根据 ID 获得三角面的顶点索引
    if(ID >= 1 && ID <= m_elements.Extent() && theNodeIDs.Length() >= 3) {
        Standard_Integer aLow = theNodeIDs.Lower();
        theNodeIDs(aLow) = m_elemNodes->Value(ID, 1);
        theNodeIDs(aLow + 1) = m_elemNodes->Value(ID, 2);
        theNodeIDs(aLow + 2) = m_elemNodes->Value(ID, 3);
        return Standard_True;
    }
    return Standard_False;
}

Standard_Boolean AMeshVS_DataSource::GetNormal(const Standard_Integer Id, const Standard_Integer Max, Standard_Real& nx, Standard_Real& ny, Standard_Real& nz) const {
    if(m_nodes.Extent() == 0) return Standard_False;

    // 获得法向
    if(Id >= 1 && Id <= m_elements.Extent() && Max >= 3) {
        nx = m_elemNormals->Value(Id, 1);
        ny = m_elemNormals->Value(Id, 2);
        nz = m_elemNormals->Value(Id, 3);
        return Standard_True;
    } else
        return Standard_False;
}